/**
 * 2025/01/01 - 2025/12/31
 */
package com.snail.server.database.engine.datasource.holder;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.snail.server.database.engine.datasource.entity.DataSource;
import com.snail.server.database.engine.datasource.enums.DataSourceTypeEnum;
import com.snail.server.database.engine.datasource.mapping.DataSourceMapping;
import com.snail.server.database.engine.datasource.service.DataSourceService;
import com.snail.starter.commons.error.CommonErrorCode;
import com.snail.starter.commons.exception.BizException;
import com.snail.server.database.engine.error.DatabaseEngineErrorCode;
import com.snail.starter.db.enums.EnableOrDisEnableEnum;
import com.snail.starter.utils.lambda.GenericEntityBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
 * @author zgadmin
 * @date 2025/8/4 13:46
 */
@Slf4j
@Component
public class DynamicDatasourceHolder {

    @Resource
    DynamicRoutingDataSource dynamicRoutingDataSource;

    @Resource
    DataSourceMapping dataSourceMapping;

    @Resource
    DataSourceService dataSourceService;

    @PostConstruct
    public void loadDB() {
        List<DataSource> dataSources = dataSourceService.selectAll(EnableOrDisEnableEnum.ENABLE);
        if (null == dataSources || dataSources.isEmpty()) {
            return;
        }
        dataSources.forEach(dataSource -> {
            try {
                loadDataSource(dataSource);
                log.info("load data source success:{}-{}", dataSource.getDataSourceId(), dataSource.getName());
            } catch (Throwable throwable) {
                log.warn("", throwable);
                log.warn("datasource load error:{}", dataSource);
            }
        });
    }

    public boolean testThrowable(String jdbcUrl, String username, String password) {
        try {
            return test(jdbcUrl, username, password);
        } catch (Throwable throwable) {
            log.warn("", throwable);
            log.warn("test connection throwable error:{},{}", jdbcUrl, username);
            throw BizException.of(CommonErrorCode.ERROR.getCode(), throwable.getMessage());
        }
    }

    public boolean test(String jdbcUrl, String username, String password) {
        try (DruidDataSource druidDataSource = GenericEntityBuilder.of(DruidDataSource::new)
                .with(DruidDataSource::setUrl, jdbcUrl)
                .with(DruidDataSource::setUsername, username)
                .with(DruidDataSource::setPassword, password)
                .with(DruidDataSource::setTestOnBorrow, true)
                .with(DruidDataSource::setValidationQuery, "SELECT 1")
                .with(DruidDataSource::setBreakAfterAcquireFailure, true)
                .with(DruidDataSource::setMaxActive, 1)
                .with(DruidDataSource::setInitialSize, 1)
                .build();
             DruidPooledConnection connection = druidDataSource.getConnection()
        ) {
            if (connection.isValid(3)) {
                return true;
            }
            log.warn("======================================== datasource connection test fail========================================\n{},{}", jdbcUrl, username);
            return false;
        } catch (Throwable throwable) {
            log.warn("", throwable);
            log.warn("test connection error:{},{}", jdbcUrl, username);
            return false;
        }
    }

    public void loadDataSource(DataSource datasource) {
        DruidDataSource druidDataSource = dataSourceMapping.dataSource2DruidDataSource(datasource);
        Map<String, Object> poolParams = null;
        if (null != datasource.getPool() && !"".equals(datasource.getPool())) {
            poolParams = JSONUtil.toBean(Base64.decodeStr(datasource.getPool()), new TypeReference<Map<String, Object>>() {
            }, true);
        }
        if (null != poolParams && !poolParams.isEmpty()) {
            Properties properties = new Properties();
            properties.putAll(poolParams);
            druidDataSource.setConnectProperties(properties);
        }
        if (test(druidDataSource.getUrl(), druidDataSource.getUsername(), druidDataSource.getPassword())) {
            dynamicRoutingDataSource.addDataSource(datasource.getDataSourceId().toString(), druidDataSource);
        }
    }

    public void unLoadDataSource(String dataSourceId) {
        if (null == dataSourceId || "".equals(dataSourceId)) {
            return;
        }
        dynamicRoutingDataSource.removeDataSource(dataSourceId);
    }

    public DataSourceTypeEnum getDataSourceType(Long datasourceId, EnableOrDisEnableEnum status) {
        DataSource datasource = dataSourceService.queryById(datasourceId, status);
        if (null == datasource) {
            throw BizException.of(DatabaseEngineErrorCode.DATASOURCE_NOT_EXISTS_OR_STATUS_ERROR, new Object[]{datasourceId, status.getDesc()});
        }
        return datasource.getType();
    }

}